import Util from '/application/Viditor/js/Util.js';

const docTypeMap = {
    Plain: ['plain', '欢迎使用Viditor，一款基于CodeMirror的在线文档编辑器！'],
    Markdown: ['markdown', '# 欢迎使用Markdown!'],
    HTML: ['htmlmixed', '<!DOCTYPE html>\n<html lang="zh-CN">\n\t<head>\n\t\t<meta charset="UTF-8">\n\t\t<meta name="viewport" content="width=device-width, initial-scale=1.0">\n\t\t<title>Document</title>\n\t</head>\n\t<body>\n\n\t</body>\n</html>'],
    CSS: ['css', '* {\n\tmargin: 0;\n\tpadding: 0;\n}'],
    JavaScript: ['javascript', 'console.log("Hello World!");']
};

const typeToExt = {
    HTML: 'html',
    CSS: 'css',
    Markdown: 'md',
    JavaScript: 'js',
    Plain: 'txt'
}

const extToType = {
    html: 'HTML',
    css: 'CSS',
    md: 'Markdown',
    js: 'JavaScript',
    txt: 'Plain'
}

const vimStatus = document.getElementById('vimStatus');
const docTypeElem = document.getElementById('docType');
const docTypeList = docType.children[1];
const content = document.getElementById('content');

const save = document.getElementById('saveFile');
const create = document.getElementById('createFile');
const inputBox = document.getElementById('inputBox');
// const vimCommandDisplay = document.getElementById('vimCommand');

const socket = parent.window.socket;
const desktop = parent.window.desktop;
const Win = parent.window.Win;

// 开启服务器消息监听
addApp();

function addApp(callback) {
    if (socket && desktop && Win) {
        // 文件在保存到服务器端后通过返回数据在桌面上新建图标
        if (callback) {
            // 先解绑事件，再绑定新的处理函数
            socket.removeListener('/application/Vititor/data/save/');
            socket.on('/application/Vititor/data/save/', (data) => {
                const app = JSON.parse(data);
                desktop.addApp(app);
                callback(app);
            });
        } else {
            // 没有callback的情况下重写事件
            // 先解绑事件，再绑定新的处理函数
            socket.removeListener('/application/Vititor/data/save/');
            socket.on('/application/Vititor/data/save/', (data) => {
                const app = JSON.parse(data);
                desktop.addApp(app);
            });
        }
    }
}

// {HTMLElement} 用于储存markdown预览窗口元素
let mdPreview;
let splitter;
let editor;
let fileName;

init();

function init() {

    // 判断是打开新文件还是链接带有查询串打开之前保存的文件
    if (window.location.href.indexOf('?') >= 0) {
        // 打开之前的文件
        const data = Util.parseQueryString(decodeURI(window.location.href));

        socket.emit('/application/Vititor/data/get/', data);
        socket.on('/application/Vititor/data/get/', (arg) => {
            const file = JSON.parse(arg);
            const docType = file.docType;
            const value = file.content;

            // 对传回来的同名同扩展名文件进行操作
            if (typeToExt[docType] === data.docType && file.fileName === data.fileName) {
                editor = setDocType(docType, value);
                fileName = data.fileName;
            }

            if (fileName) {
                // 已经由文件名的时候是自动保存，这个按钮就无效（或者设置为另存为）
                save.children[0].innerHTML = '自动保存';
                save.children[0].className = 'disabled';
                // 文件类型切换也不允许
                docTypeElem.children[0].className = 'disabled';
            }
        });
    } else {
        // DOTO 新建文件
        editor = setDocType('Plain');
    }
}

// 文档类型选择菜单弹出事件
docTypeElem.children[0].addEventListener('click', (event) => {
    event.preventDefault();
    clearAllMenus();
    // 有文件名的时候不允许点击，切换文件类型
    if (!fileName) {
        docTypeList.style.display = 'block';
        Util.animate(docTypeList, {
            opacity: 1,
        });
    }
});

function setDocType(type, val) {
    let value;

    if (val) {
        value = val;
    } else {
        try {
            // 编辑器已经初始化
            value = editor.getValue();
            // 删除原有编辑器
            document.getElementsByClassName('CodeMirror')[0].remove();
        } catch {
            // 编辑器未初始化
            value = docTypeMap[type][1];
        }
    }

    // 新建编辑器
    const newEditor = CodeMirror.fromTextArea(content, {
        value: value,
        mode: docTypeMap[type][0],
        lineNumbers: true,
        smartIndent: true,
        indentWithTabs: true,
        theme: 'dracula',
        indentUnit: 4,
        scrollbarStyle: 'simple',
        keyMap: 'vim',
        // lineWrapping: true,
        styleActiveLine: true,
        autoCloseBrackets: true,
    });

    // 修改显示文字
    docTypeElem.children[0].innerText = type;
    // 关闭所有菜单
    clearAllMenus();
    // 修改编辑器文字
    setDefaultValue(newEditor, type, value);

    if (type === 'HTML' || type === 'CSS') {
        emmetCodeMirror(newEditor);
    }

    if (type === 'Markdown') {
        // 设置markdown的实时预览
        marked.setOptions({
            highlight: function (code, lang) {
                return hljs.highlightAuto(code).value;
            }
        });

        mdPreview = document.createElement('div');
        mdPreview.setAttribute('id', 'mdPreview');
        mdPreview.setAttribute('class', 'markdown-body');

        const CMElem = document.getElementsByClassName('CodeMirror')[0];

        CMElem.style.width = '50%';
        const mainElem = CMElem.parentElement;

        splitter = document.createElement('div');
        splitter.setAttribute('id', 'splitter');

        splitter.addEventListener('mousedown', function (event) {
            document.body.style.cursor = 'col-resize';
            // 保存当前位置 
            let currentWidth, prevWidth;

            const _this = this;
            window.onmousemove = function (event) {
                currentWidth = (event.clientX * 100) / mainElem.offsetWidth;
                prevWidth = 100 - currentWidth;
                if (currentWidth <= 70 && currentWidth >= 30) {
                    mdPreview.style.width = prevWidth + '%';
                    CMElem.style.width = currentWidth + '%';
                    _this.style.left = currentWidth + '%';
                }
            }
            // 解绑事件
            window.onmouseup = function () {
                window.onmousemove = null;
                document.body.style.cursor = 'auto';
            }
        });

        content.parentElement.appendChild(splitter);
        content.parentElement.appendChild(mdPreview);

        mdPreview.innerHTML = marked(newEditor.getValue());
        newEditor.on('keyup', () => {
            mdPreview.innerHTML = marked(newEditor.getValue());
        });
    } else {
        // 关闭markdown的预览窗口
        document.getElementsByClassName('CodeMirror')[0].style.width = '100%';
        if (mdPreview) {
            mdPreview.remove();
        }

        if (splitter) {
            splitter.remove();
        }
    }

    newEditor.on('vim-mode-change', (modeObj) => {
        vimStatus.innerText = modeObj.mode.toUpperCase();
    });

    newEditor.on('focus', () => {
        // 关闭文档类型选择菜单
        clearAllMenus();
    });

    let timer;
    newEditor.on('keyup', () => {
        // 设置了文件名的才上传
        if (fileName) {
            if (timer) clearTimeout(timer);
            // 设置延时上传保存
            timer = setTimeout(() => {
                const content = newEditor.getValue();
                if (contentChanged(content) && socket) {
                    socket.emit('/application/Vititor/data/save/', {
                        user: JSON.parse(window.localStorage['user']),
                        fileName: fileName,
                        text: content,
                        docType: type,
                    });
                }
            }, 1000);
        }
    });

    return newEditor;
}

function contentChanged(oldValue) {
    for (let t in docTypeMap) {
        if (!oldValue || docTypeMap[t][1] === oldValue) {
            return false;
        }
    }
    return true;
}

function setDefaultValue(editor, type, oldValue) {
    // 编辑器内容为空或者是默认文字的时候修改文字内容
    if (!contentChanged(oldValue)) {
        editor.setValue(docTypeMap[type][1]);
        editor.clearHistory();
    } else {
        editor.setValue(oldValue);
        editor.clearHistory();
    }
}

function clearAllMenus() {
    docTypeList.style.opacity = 0;
    inputBox.style.opacity = 0;
    docTypeList.style.display = 'none';
    inputBox.style.display = 'none';
    inputBox.value = '';
}

// 在有文件名的情况下禁用
docTypeList.addEventListener('click', (event) => {
    event.preventDefault();
    const type = event.target.innerText;

    if (type) {
        editor = setDocType(type);
    };
});

create.children[0].addEventListener('click', (event) => {
    event.preventDefault();
    fileOperation(createFile, 'Create');
});

save.children[0].addEventListener('click', (event) => {
    event.preventDefault();
    clearAllMenus();
    if (!fileName) {
        fileOperation(saveFile, 'Save');
    }
});

inputBox.addEventListener('blur', (event) => {
    clearAllMenus();
});

// 自定义快捷键
let keys = '';
window.addEventListener('keydown', function (event) {
    if (event.key === 'Control') {
        keys = 'Control';
    }
    if (keys === 'Control' && event.key === '/') {
        // html和css使用Emmet插件的注释功能
        if (editor.options.mode === 'htmlmixed' || editor.options.mode === 'css') return;
        editor.toggleComment();
    }
});

window.addEventListener('keyup', function (event) {
    if (event.key === 'Control') {
        keys = '';
    }
});

function fileOperation(fn, type) {
    clearAllMenus();

    // 初始化样式
    inputBox.setAttribute('placeholder', `${type}: Enter file name .txt .md .html .css .js`);
    inputBox.className = '';
    inputBox.style.display = 'block';
    inputBox.focus();

    Util.animate(inputBox, {
        opacity: 1,
    });
    window.onkeyup = function (event) {
        if (event.keyCode === 13) {
            const fileName = inputBox.value;
            // 重名检查
            for (let id in desktop.app) {
                if (desktop.app[id].name === fileName) {
                    inputBox.className = 'invalid';
                    inputBox.setAttribute('placeholder', 'File name exists!');
                    inputBox.value = '';
                    return;
                }
            }

            const flag = fn(fileName);
            if (!flag) {
                inputBox.className = 'invalid';
                inputBox.setAttribute('placeholder', 'Invalid file name or unsupported extension!');
            } else {
                clearAllMenus();
                window.onkeyup = null;
            }
            inputBox.value = '';
        }
    }

    window.onkeydown = function (event) {
        inputBox.className = '';
        inputBox.setAttribute('placeholder', `${type}: .txt .md .html .css .js`);
    }
}

// 重新创建一个新的编辑器窗口
function createFile(fName) {
    if (isValidFileName(fName)) {
        const temp = fName.split('.');
        const ext = temp[temp.length - 1];
        setNewFile(fName, docTypeMap[extToType[ext]][1], (data) => {
            Win.open(desktop, data.id);
        });
        return true;
    } else {
        return false;
    }
}

// 如果已经有用户名，则方法无效，因为自动保存(考虑添加另存为的方法)
// 如果是新的窗口，没有用户名，则同样打开新窗口，内容为当前内容，同时关闭旧窗口
function saveFile(fName) {
    if (isValidFileName(fName)) {

        setNewFile(fName, editor.getValue(), (data) => {
            Win.open(desktop, data.id);
            try {
                // 关闭当前窗口
                Win.closeByName('Viditor');
            } catch {
                // 由窗口另存为的时候不需要关闭原来的窗口
            }
        });
        return true;
    } else {
        return false;
    }
}

function setNewFile(fName, value, callback) {

    const dotIndex = fName.lastIndexOf('.');
    const ext = fName.substring(dotIndex + 1);
    const name = fName.substring(0, dotIndex);
    socket.emit('/application/Vititor/data/save/', {
        user: JSON.parse(window.localStorage['user']),
        fileName: name,
        text: value,
        docType: extToType[ext],
    });
    addApp((data) => {
        callback && callback(data);
    });
}

// 判断是否是有效的文件名：不能带有中文，必须有扩展名，扩展名必须是当前支持的类型，不能有空格
function isValidFileName(fileName) {

    // 用户名为空或长度超过限制
    if (fileName === '' || fileName.length > 255) return false;

    const dotIndex = fileName.lastIndexOf('.');
    const ext = fileName.substring(dotIndex + 1);
    const name = fileName.substring(0, dotIndex);

    const pattern = /^[^\s]{1}[^-_\~!@#\$%\^&\*\.\(\)\[\]\{\}<>\?\\\/\'\"]*$/;
    // 正则验证不通过
    if (!pattern.exec(name)) return false;

    // 暂时不支持该文件后缀
    if (extToType[ext] === undefined) return false;

    return true;
}
